#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/fb.h>
#include <jpeglib.h>
#include <string.h>
// bmp 太大了 通过jpeg 和 png 进行压缩一下
// 原图-> 压缩算法（jpg 或者jpeg）-> 解码才能显示


// 假如我给定一个图片 我想让他竖着显示该怎么做呢
// 假如分辨率是 800 *600 我给定一个600*800的
typedef struct bgr888_color {
    unsigned char red;
    unsigned char green;
    unsigned char blue;
} __attribute__ ((packed)) bgr888_t;
static int width; // LCD x 分辨率
static int height; //LCD y 分辨率
static unsigned short *screen_base = NULL; //映射后的显存基地址
static unsigned long line_length; //LCD 一行的长度（字节为单位）
static unsigned int bpp; //像素深度 bpp

// 显示jpeg 图像
// index 表明现在是图像的第几行
// min_h 竖屏显示的高度：原来的宽度
// min_w  竖屏显示的宽度：原来的高度
//?  完蛋晕了 明天再说
// 除非大小对得上要不对不上
void show_rotate( bgr888_t* jpeg_line_buf,unsigned short* fb_line_buf,\
                    int min_w,int min_h,int index)
{
    // 转换一下格式
    for (int i = 0; i < min_h; i++)
        fb_line_buf[i] = ((jpeg_line_buf[i].red & 0xF8) << 8) |
                        ((jpeg_line_buf[i].green & 0xFC) << 3) |
                        ((jpeg_line_buf[i].blue & 0xF8) >> 3);
    for(int i = 0; i < min_h; i++) {
        screen_base[index + i * min_w] = fb_line_buf[min_h -i-1];
    }
}
static int show_jpeg_image(const char* path)
{
    struct jpeg_decompress_struct cinfo;
    struct jpeg_error_mgr jerr;
    FILE *jpeg_file = NULL;
    bgr888_t *jpeg_line_buf = NULL; //行缓存区 用于存储从jpeg解压出来的一行数据
    unsigned short* fb_line_buf = NULL; ///行缓冲区:用于存储写入到 LCD 显存的一行数据
    unsigned int min_h,min_w;
    unsigned int valid_bytes;
    int i;

    // 绑定默认错误处理函数
    cinfo.err = jpeg_std_error(&jerr);
    // 打开jpeg / .jpg 图像文件
    jpeg_file = fopen(path, "r");
    if (NULL == jpeg_file) {
        perror("fopen error");
        return -1;
    }

    //创建 JPEG 解码对象
    jpeg_create_decompress(&cinfo);
    //指定图像文件
    jpeg_stdio_src(&cinfo, jpeg_file);

    //读取图像信息
    jpeg_read_header(&cinfo, TRUE);
    
    printf("jpeg 图像大小: %d*%d\n", cinfo.image_width, cinfo.image_height);

    // 解码的一些参数 -- 具体到时候可以搜索
    cinfo.out_color_space = JCS_RGB;
    
    //开始解码图像
    jpeg_start_decompress(&cinfo);


     //为缓冲区分配内存空间
     //output_components 对于RGB来说这玩意就是3
    jpeg_line_buf = malloc(cinfo.output_components * cinfo.output_width);
    fb_line_buf = malloc(line_length);

     //判断图像和 LCD 屏那个的分辨率更低
     //  图片 600 *800  宽度 600 高度800
     // 图像的高度比
    if (cinfo.output_height > width)
        min_w = width;
    else
        min_w = cinfo.output_height;

    if (cinfo.output_width > height)
        min_h = height;
    else
        min_h = cinfo.output_width;


    int index=0;
    while(cinfo.output_scanline < min_w) {

        jpeg_read_scanlines(&cinfo,(unsigned char**)&jpeg_line_buf,1);
        //将读取到的 BGR888 数据转为 RGB565
        // 这里应该直接传屏幕的宽度才行
        show_rotate(jpeg_line_buf,fb_line_buf,width,min_h,index);
        index++;
    }
    printf("index %d\r\n",index);
    // 解码完成
    jpeg_finish_decompress(&cinfo);
    jpeg_destroy_decompress(&cinfo);//销毁 JPEG 解码对象、释放资源
    //关闭文件、释放内存
    fclose(jpeg_file);
    free(fb_line_buf);
    free(jpeg_line_buf);
    return 0;
}
int main(int argc,char* argv[])
{
    struct fb_fix_screeninfo fb_fix;
    struct fb_var_screeninfo fb_var;
    unsigned int screen_size;
    int fd;

    /* 传参校验 */
    if (2 != argc) {
        fprintf(stderr, "usage: %s <jpeg_file>\n", argv[0]);
        exit(-1);
    }
    /* 打开framebuffer设备*/
    if(0 > (fd = open("/dev/fb0",O_RDWR))) {
        perror("open error");
        exit(EXIT_FAILURE);
    }
    /* 获取参数信息*/
    ioctl(fd,FBIOGET_VSCREENINFO,&fb_var);
    ioctl(fd,FBIOGET_FSCREENINFO,&fb_fix);

    // 设定屏幕信息
    line_length = fb_fix.line_length; // 一行用到的字节数
    bpp = fb_var.bits_per_pixel;    // ,每个像素用了多少bit的数据
    screen_size = line_length * fb_var.yres;
    width = fb_var.xres;
    height = fb_var.yres;

    /* 将显示缓冲区映射到进程地址空间 */
    screen_base = mmap(NULL, screen_size, PROT_WRITE, MAP_SHARED, fd, 0);
    if (MAP_FAILED == (void *)screen_base) {
        perror("mmap error");
        close(fd);
        exit(EXIT_FAILURE);
    }
    /* 显示 BMP 图片 */
    memset(screen_base, 0xFF, screen_size);
    show_jpeg_image(argv[1]);
    /* 退出 */
    munmap(screen_base, screen_size); //取消映射
    close(fd); //关闭文件
    exit(EXIT_SUCCESS); //退出进程

}